﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework.Content;

namespace TNCGameEngine.Content
{
    public class SharedContentManager : ContentManager
    {

        private static CommonContentManager common;
        private List<string> loadedAssets;


        public new string RootDirectory
        {
            get { return SharedContentManager.common.RootDirectory; }
            set { SharedContentManager.common.RootDirectory = value; }
        }

        public new IServiceProvider ServiceProvider
        {
            get { return SharedContentManager.common.ServiceProvider; }
        }

        public SharedContentManager(IServiceProvider serviceProvider, string rootDirectory)
            : base(serviceProvider, rootDirectory)
        {
            // ensure initialization
            if (SharedContentManager.common == null)
            {
                SharedContentManager.common = new CommonContentManager(serviceProvider, rootDirectory);
            }
            this.loadedAssets = new List<string>();
        }

        internal static string GetCleanPath(string path)
        {
            int num2;
            path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
            for (int i = 1; i < path.Length; i = Math.Max(num2 - 1, 1))
            {
                i = path.IndexOf(@"\..\", i);
                if (i < 0)
                {
                    return path;
                }
                num2 = path.LastIndexOf(Path.DirectorySeparatorChar, i - 1) + 1;
                path = path.Remove(num2, (i - num2) + @"\..\".Length);
            }
            return path;
        }

        internal T GetContent<T>(string assetName)
        {
            return SharedContentManager.common.GetContent<T>(assetName);
        }

        public override T Load<T>(string assetName)
        {
            assetName = GetCleanPath(assetName);
            this.loadedAssets.Add(assetName);
            return SharedContentManager.common.Load<T>(assetName);
        }

        public override void Unload()
        {
            if (this.loadedAssets == null)
                throw new ObjectDisposedException(typeof(SharedContentManager).Name);

            SharedContentManager.common.Unload(this);
            this.loadedAssets = null;

            base.Unload();
        }

        public void UnloadSingleContent(object content)
        {
            SharedContentManager.common.UnloadSingleContent(this, content);
        }

        private class CommonContentManager : ContentManager
        {

            private readonly Dictionary<string, ReferencedAsset> references;

            public CommonContentManager(IServiceProvider serviceProvider, string rootDirectory)
                : base(serviceProvider, rootDirectory)
            {
                this.references = new Dictionary<string, ReferencedAsset>();
            }

            public T GetContent<T>(string assetName)
            {
                return (T)this.references[assetName].Asset;
            }
            
            public override T Load<T>(string assetName)
            {
                assetName = GetCleanPath(assetName);

                ReferencedAsset refAsset;
                if (!references.TryGetValue(assetName, out refAsset))
                {
                    refAsset = new ReferencedAsset { Asset = ReadAsset<T>(assetName, null) };
                    references.Add(assetName, refAsset);
                }
                refAsset.References++;

                return (T)refAsset.Asset;
            }

            public void Unload(SharedContentManager container)
            {
                foreach (string assetName in container.loadedAssets)
                {
                    ReferencedAsset refAsset = references[assetName];
                    refAsset.References--;
                    if (refAsset.References == 0)
                    {
                        if (refAsset.Asset is IDisposable)
                            (refAsset.Asset as IDisposable).Dispose();
                        references.Remove(assetName);
                    }
                }
            }

            public void UnloadSingleContent(SharedContentManager container, object obj)
            {

                string s = string.Empty;

                foreach (KeyValuePair<string, ReferencedAsset> kvp in this.references)
                {
                    ReferencedAsset refAsset = kvp.Value;
                    if (refAsset.Asset == obj)
                    {
                        s = kvp.Key;
                        refAsset.References--;
                        if (refAsset.References == 0)
                        {
                            if (refAsset.Asset is IDisposable)
                                (refAsset.Asset as IDisposable).Dispose();
                            references.Remove(s);
                        }
                        break;
                    }
                }

                for (int i = container.loadedAssets.Count -1; i >=0; i--)
                {
                    if (container.loadedAssets[i].Equals(s))
                    {
                        container.loadedAssets.RemoveAt(i);
                    }
                }

            }

            private class ReferencedAsset
            {
                public object Asset;
                public int References;
            }

        }
    }
}